<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>7.2.1-渲染器的基本概念</title>
</head>

<body>
    <div id="app"></div>
</body>
    <script src="https://unpkg.com/@vue/reactivity@3.0.5/dist/reactivity.global.js"></script>
    <script>
        /**
         * 理解渲染器所涉及的基本概念，有利于理解后续内容
         * 
         * 通常使用英文 renderer 来表达“渲染器”
         * - 不要把 renderer 和 render 弄混了，前者代表渲染器，而后者是动词，表示 “渲染”
         * - 渲染器的作用是把虚拟 DOM 渲染为特定平台上的真实元素
         * - 在浏览器平台上，渲染器会把虚拟 DOM 渲染为真实 DOM 元素。
         * 
         * 虚拟 DOM 通常用英文 virtual DOM 来表达，有时会简写成 vdom
         * - 虚拟 DOM 和真实 DOM 的结构一样，都是由一个个节点组成的树型结构
         * - “虚拟节点” 这样的词，即 virtual node，有时会简写成 vnode
         * - 虚拟 DOM 是树型结构，这棵树中的任何一个 vnode 节点都可以是一棵子树，
         *    因此 vnode 和 vdom 
         * - 统一使用 vnode
         * 
         * 渲染器把虚拟 DOM 节点渲染为真实 DOM 节点的过程叫作挂载，通常用英文 mount 来表达
         * - 在 mounted 钩子中可以访问真实 DOM 元素
         * 
         * 渲染器把真实 DOM 挂载到哪里
         * - 渲染器通常需要接收一个挂载点作为参数，用来指定具体的挂载位置
         * - 这里的 “挂载点” 其实就是一个 DOM 元素，
         *    渲染器会把该 DOM 元素作为容器元素，并把内容渲染到其中
         * - 用英文 container 来表达容器
         * 
         * 为什么需要 createRenderer 函数
         * - 渲染器与渲染是不同的
         * - 渲染器是更加宽泛的概念，它包含渲染
         * - 渲染器不仅可以用来渲染，还可以用来激活已有的 DOM 元素，
         *    这个过程通常发生在同构渲染的情况下
         * - 渲染器的内容非常广泛，而用来把 vnode 渲染为真实 DOM 的 render 函数只是其中一部分
         * - 甚至连创建应用的 createApp 函数也是渲染器的一部分。
         * 
         * 调用 createRenderer 函数创建一个渲染器，接着调用渲染器的 renderer.render 函数执行渲染
         * - 首次调用 renderer.render 函数时，
         *    只需要创建新的 DOM 元素即可，这个过程只涉及挂载。
         * - 当多次在同一个 container 上调用 renderer.render 函数进行渲染时，
         *    渲染器除了要执行挂载动作外，还要执行更新动作
         * 
         * “打补丁”（或更新），英文通常用 patch 来表达
         * - 渲染器会使用 newVNode 与上一次渲染的 oldVNode 进行比较，试图找到并更新变更点
         * - 实际上，挂载动作本身也可以看作一种特殊的打补丁，它的特殊之处在于旧的 vnode 是不存在的
         *    所以我们不必过于纠结 “挂载” 和 “打补丁” 这两个概念
         * 
         * patch 函数是整个渲染器的核心入口，它承载了最重要的渲染逻辑
         * - 在首次渲染时传递给 patch 函数的第一个参数 n1 也是 undefined
         * - 这时，patch 函数会执行挂载动作，它会忽略 n1，并直接将 n2 所描述的内容渲染到容器中
         * - patch 函数不仅可以用来完成打补丁，也可以用来执行挂载。
         * 
         * */ 

        function createRenderer() { // 新增
            function render(vnode, container) {
                if (vnode) {
                    // 新 vnode 存在，将其与旧 vnode 一起传递给 patch 函数，进行打补丁
                    patch(container._vnode, vnode, container)
                } else {
                    if (container._vnode) {
                        // 旧 vnode 存在，且新 vnode 不存在，说明是卸载（unmount）操作
                        // 只需要将 container 内的 DOM 清空即可
                        container.innerHTML = ''
                    }
                }
                // 把 vnode 存储到 container._vnode 下，即后续渲染中的旧 vnode
                container._vnode = vnode
            }

            return {
                render
            }
        }
    </script>
    

</html>